/*******************************************************************************
 * Copyright (c) 2001, 2002 Boris Pruessmann and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v0.5
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors:
 *    Boris Pruessmann - Initial implementation.
 *    David Corbin - Add support for creating depot "connections" in all cases
 ******************************************************************************/
package net.sourceforge.perforce.ui.wizards;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.api.IPerforceServerManager;
import net.sourceforge.perforce.core.internal.P4TeamProvider;
import net.sourceforge.perforce.core.internal.events.AddDeleteMoveListener;
import net.sourceforge.perforce.ui.MicroPluginFactory;
import net.sourceforge.perforce.ui.PerforceDecorator;
import net.sourceforge.perforce.ui.PerforceUIPlugin;
import net.sourceforge.perforce.ui.internal.Policy;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.ui.IConfigurationWizard;
import org.eclipse.ui.IWorkbench;

/**
  * @version $Revision: 68 $
  * @author  <a href="mailto:bpruessmann@users.sourceforge.net">Boris Pruessmann</a>
  * @author  <a href="mailto:dcorbin@users.sourceforge.net">David Corbin</a>
  */
public class SharingWizard extends Wizard implements IConfigurationWizard
{
	// --------------------------------------------------------------------------- Private Variables
	
	/** Stores a referene to the project that we want to share. */
	private IProject project;
	/** Stores the reference to the ConnectProjectPage. */
	private ConnectProjectPage connectProjectPage;	
	/** Stores the tasks to be performed. */
	private List tasks;

	// ------------------------------------------------------------------------- Public Constructors

	/** Constructor for SharingWizard. */
	public SharingWizard()
	{
		setNeedsProgressMonitor(true);
	}
	
	// ------------------------------------------------------------------------------ Public Methods
	
	/** @see IConfigurationWizard#init(IWorkbench, IProject) */
	public void init(IWorkbench workbench, IProject project)
	{
		Policy.assertNotNull(project);		
		this.project = project;
	}
	
	/** @see Wizard#canFinish() */
	public boolean canFinish()
	{
		if (!connectProjectPage.canFinish())
		{			
			return false;
		}
		return super.canFinish();
	}

	/** @see IWizard#performFinish() */
	public boolean performFinish()
	{
		try 
		{
			assembleTasks();
			runTasks();
				
			return true;
		} 
		catch (InterruptedException e) 
		{
			handle(e);
			
			return false;
		}
		catch (InvocationTargetException e) 
		{
			Throwable target = e.getTargetException();
			handle(target);
				
			return false;
		} 
	}

	/** @see IWizard#addPages() */
	public void addPages()
	{
		connectProjectPage = 
			new ConnectProjectPage(MicroPluginFactory.getPlugin(), project.getLocation()); 
		addPage(connectProjectPage);
	}
	
	// ----------------------------------------------------------------------------- Private Methods
	
	/** Call this method to assemble a list of tasks to be performed to share the project. */
	private void assembleTasks()
	{
		tasks = new ArrayList();

		IPath originalLoc = getProjectLocation();
		IPath newLoc = connectProjectPage.getProjectLocation();
		if (!originalLoc.equals(newLoc))
		{
			tasks.add(new MoveProject(project, connectProjectPage.getProjectLocation()));
		}
		tasks.add(new ShareProject(project, connectProjectPage.getServer()));
		tasks.add(new CacheSyncInfoTask(project, connectProjectPage.getServer()));
	}
	
	/** Call this method to run the tasks to share the project. 
	  * 
	  * @throws InvocationTargetException in case of an error.
	  * @throws InterruptedException in case of an error.
	  */
	private void runTasks()
		throws InvocationTargetException, InterruptedException
	{
		Policy.assertNotNull(tasks);
		
		getContainer().run(false, false, new IRunnableWithProgress()
		{
			public void run(IProgressMonitor monitor)
				throws InvocationTargetException, InterruptedException
			{
				try 
				{
					monitor.beginTask("", 1000 * tasks.size());
					for (Iterator it = tasks.iterator(); it.hasNext();)
					{
						Task task = (Task) it.next();
						task.run(Policy.subMonitorFor(monitor, 1000));
					}
				} 
				finally
				{
					monitor.done();
				}
			}
		});
	}

	/** Call this method to retrieve the project's location.
	  * 
	  * @return the location of the project.
	  */
	private IPath getProjectLocation() 
	{
		return project.getLocation();
	}
	
	/** Handles exceptions that occur in Perforce Sharing Wizard.
	  * 
	  * @param t exception that should be handled.
	  */
	protected void handle(Throwable t) 
	{
		if (t instanceof InvocationTargetException)
		{
			t = ((InvocationTargetException) t).getTargetException();
		}
		
		IStatus error = null;
		if (t instanceof CoreException) 
		{
			error = ((CoreException) t).getStatus();
		}
		else 
		if (t instanceof TeamException) 
		{
			error = ((TeamException) t).getStatus();
		}
		else 
		{
			error = new Status(Status.ERROR, PerforceUIPlugin.ID, 1, 
				Policy.bind("simpleInternal"), t);                                     //$NON-NLS-1$
		}
	
		Shell shell = getContainer().getShell();
	
		if (error.getSeverity() == IStatus.INFO)
		{
			MessageDialog.openInformation(shell, Policy.bind("information"),           //$NON-NLS-1$
			                              error.getMessage());
		}
		else
		{
			ErrorDialog.openError(shell, Policy.bind("exception"), null, error);       //$NON-NLS-1$
		}
		
		shell.dispose();

		// Let's log non-team exceptions
		if (!(t instanceof TeamException)) 
		{
			PerforceUIPlugin.log(error);
		}
	}

	// ------------------------------------------------------------------- Private Nested Interfaces

	/** Describes a Task that has to be performed to share a project. */
	private static interface Task
	{
		/** Executes the task. 
		  * 
		  * @param monitor for progress feedback.
		  * @throws InvocationTargetException in case of an error.
		  * @throws InterruptedException in case of an error,
		  */
		void run(IProgressMonitor monitor)
			throws InvocationTargetException, InterruptedException;
	}
	
	// ---------------------------------------------------------------------- Private Nested Classes
	
	/** @see Task */
	private static class ShareProject implements Task
	{
		// ----------------------------------------------------------------------- Private Variables
		
		/** Stores the project instance. 
		  *
		  * TODO: Perhaps this is better an inner class. 
		  */
		private IProject project;
		/** Stores the server instance. */
		private IPerforceServer server;
		
		// --------------------------------------------------------------------- Public Constructors
		
		/** Constructor for ShareProject.
		  *
		  * @param project the project to share. 
		  * @param server Server that is used to share the project.
		  */
		public ShareProject(IProject project, IPerforceServer server)
		{
			this.project = project;
			this.server = server;
		}
		
		// -------------------------------------------------------------------------- Public Methods
		
		/** @see Task#run(IProgressMonitor, Shell) */
		public void run(IProgressMonitor monitor)
			throws InvocationTargetException, InterruptedException
		{
			try 
			{
				monitor.beginTask("", 1000);
				
				IPerforceServerManager manager = PerforceCorePlugin.getServerManager();
				manager.setSharing(project, server, monitor);
			}	
			catch (TeamException e) 
			{
				throw new InvocationTargetException(e);
			}
			finally
			{
				monitor.done();
			}
		}		
	}
	
	/** @see Task */
	private static class MoveProject implements Task
	{
		// ----------------------------------------------------------------------- Private Variables
		
		/** Stores the project to move. */
		private IProject project;
		/** Stores the project's new location. */
		private IPath newLocation;
		
		// --------------------------------------------------------------------- Public Constructors
		
		/** Constructor for MoveProject.
		  *
		  * @param project project to move.
		  * @param newLocation the project's new location.
		  */
		public MoveProject(IProject project, IPath newLocation)
		{
			this.project = project;
			this.newLocation = newLocation;
		}
		
		// -------------------------------------------------------------------------- Public Methods
		
		/** @see Task#run(IProgressMonitor, Shell) */
		public void run(IProgressMonitor monitor)
			throws InvocationTargetException, InterruptedException
		{
				try
				{
					monitor.beginTask("", 1000);
					
					IProjectDescription description = project.getDescription();
					description.setLocation(newLocation);
					project.move(description, true, monitor);
				}
				catch (CoreException e)
				{
					throw new InvocationTargetException(e);
				}
				finally
				{
					monitor.done();
				}
		}		
	}
	
	/** Task that collects the cache information. */
	private static class CacheSyncInfoTask implements Task
	{
		// ----------------------------------------------------------------------- Private Variables
		
		/** Stores the project handle. */
		private IProject project;
		/** Stores the perforce server handle. */
		private IPerforceServer server;
		
		// --------------------------------------------------------------------- Public Constructors
		
		/** Constructor for CacheSyncInfoTask.
		  *
		  * @param newProject the project. 
		  * @param newServer the perforce server.
		  */
		public CacheSyncInfoTask(IProject newProject, IPerforceServer newServer)
		{
			project = newProject;
			server = newServer;
		}
		
		// -------------------------------------------------------------------------- Public Methods
		
		/** @see SharingWizard.Task#run(IProgressMonitor) */
		public void run(IProgressMonitor monitor)
			throws InvocationTargetException, InterruptedException 
		{
			try
			{
				P4TeamProvider provider = 
					(P4TeamProvider) RepositoryProvider.getProvider(project);
				
				provider.refreshState(
					new IResource[] { project }, IResource.DEPTH_INFINITE, monitor);
					
				PerforceDecorator.refresh();
				AddDeleteMoveListener.refreshAllMarkers();
			}
			catch (CoreException e)
			{
				throw new InvocationTargetException(e);
			}
			catch (TeamException e)
			{
				throw new InvocationTargetException(e);
			}
			finally
			{
				monitor.done();
			}
		}

	}
}
